package com.duang.framework.service;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;

import com.duang.framework.core.Service;
import com.duang.framework.util.IntrospectionUtils;

/**
 * @author Administrator
 * 
 */
public final class ServiceUtils {

	public static Method[] findServiceMethods(Class<?> clazz,
			List<Method[]> methodImplList) {
		Method[] methodDefines = null;
		Class<?> superC = clazz;
		find: while (!superC.equals(BaseServiceTemplate.class)) {
			Class<?>[] interfaces = superC.getInterfaces();
			Method[] methods = superC.getDeclaredMethods();
			methodImplList.add(methods);
			if (interfaces.length > 0) {
				for (Class<?> interfaceClazz : interfaces) {
					if (interfaceClazz.isAnnotationPresent(Service.class)) {
						methodDefines = interfaceClazz.getDeclaredMethods();
						break find;
					}
				}
			}
			superC = superC.getSuperclass();
		}
		return methodDefines;
	}

	public static Class<?> buildServiceProxyWithJavassist(Class<?> superClass,
			Method[] methodNeedToImplement, Class<?> daoClazz) throws Exception {
		return buildServiceProxyWithJavassist(superClass,
				Arrays.asList(methodNeedToImplement), daoClazz);
	}

	public static Class<?> buildServiceProxyWithJavassist(Class<?> superClass,
			List<Method> methodNeedToImplement, Class<?> daoClazz)
			throws Exception {
		ClassPool cp = ClassPool.getDefault();
		CtClass ctClass = cp.makeClass("com.jduang.service.impl."
				+ superClass.getName() + "$Impl");

		if (daoClazz != null) {
			CtConstructor constructor = new CtConstructor(
					new CtClass[] { cp.getCtClass(daoClazz.getName()) },
					ctClass);
			constructor.setBody("{\n this.dao=$1; \n}");
			ctClass.addConstructor(constructor);
			CtField ctField = new CtField(cp.getCtClass(daoClazz.getName()),
					"dao", ctClass);
			ctClass.addInterface(cp.getCtClass(superClass.getName()));
			ctClass.addField(ctField);
		} else {
			ctClass.setSuperclass(cp.getCtClass(superClass.getName()));
		}
		StringBuffer body = null;

		for (Method method : methodNeedToImplement) {
			Class<?>[] parameterTypes = method.getParameterTypes();
			CtClass[] arguments = new CtClass[parameterTypes.length];
			for (int i = 0; i < parameterTypes.length; i++) {
				arguments[i] = cp.getCtClass(parameterTypes[i].getName());
			}
			CtMethod ctMethod = new CtMethod(cp.getCtClass(method
					.getReturnType().getName()), method.getName(), arguments,
					ctClass);
			ctMethod.setModifiers(Modifier.PUBLIC);
			body = new StringBuffer();
			body.append("{\n");
			if (void.class.isAssignableFrom(method.getReturnType())) {
				body.append("dao.");
				body.append(method.getName());
				body.append("(");
				for (int i = 1; i <= parameterTypes.length; i++) {
					body.append("$" + i);
					if (i != parameterTypes.length) {
						body.append(",");
					}
				}
				body.append(");");
			} else {
				body.append("return ");
				body.append("dao.");
				body.append(method.getName());
				body.append("(");
				for (int i = 1; i <= parameterTypes.length; i++) {
					body.append("$" + i);
					if (i != parameterTypes.length) {
						body.append(",");
					}
				}
				body.append(");");

			}
			body.append("\n}");
			System.out.println(body);
			ctMethod.setBody(body.toString());
			ctClass.addMethod(ctMethod);
		}
		Class<?> c = ctClass.toClass();
		return c;

	}

	public static void filterMethodToBeImpled(List<Method[]> methodImplList,
			List<Method> methodNeedToImplement) {
		for (Method[] methodImpls : methodImplList) {
			for (Method methodImpl : methodImpls) {
				if ((methodImpl.getModifiers() & Modifier.ABSTRACT) == 0) {
					String key = IntrospectionUtils
							.getMethodSignKeyInClass(methodImpl);
					Iterator<Method> iterator = methodNeedToImplement
							.iterator();
					while (iterator.hasNext()) {
						Method methodDefine = iterator.next();
						String methodKey = IntrospectionUtils
								.getMethodSignKeyInClass(methodDefine);
						if (key.equals(methodKey)) {
							iterator.remove();
						}

					}
				}

			}
		}

	}

	public static String getMethodCacheKey(Object[] arguments) {
		StringBuilder sb = new StringBuilder();
		if ((arguments != null) && (arguments.length != 0)) {
			for (int i = 0; i < arguments.length; i++) {
				sb.append(".").append(arguments[i]);
			}
		}
		return sb.toString();
	}

	/**
	 * @deprecated
	 * @param targetName
	 * @param methodName
	 * @param arguments
	 * @return
	 */
	public static String getCacheKey(String targetName, String methodName,
			Object[] arguments) {
		StringBuilder sb = new StringBuilder();
		sb.append(targetName).append(".").append(methodName);
		if ((arguments != null) && (arguments.length != 0)) {
			for (int i = 0; i < arguments.length; i++) {
				sb.append(".").append(arguments[i]);
			}
		}
		return sb.toString();
	}
}
